package core;

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import persistence.DataPersistor;

import core.exception.IllegalAddException;
import core.exception.IllegalEditException;
import core.exception.IllegalRemoveException;
import core.exception.NotFoundException;
import core.exception.SaveException;

public class ContainerFacadeTest {
	
	static ContainerFacade facade;
	static Container testSU;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		DataPersistor.instance().init("-serial");
		ContainerFacade.clearAllData();
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
		facade = ContainerFacade.instance();
		facade.addStorageUnit("TestSU");
		testSU = facade.get_rootUnit().lookupContainer("TestSU");
	}

	@After
	public void tearDown() throws Exception {
		RootStorageUnit.setInstance(null);
		ContainerFacade.setInstance(null);
	}

	@Test
	public void testCanRenameContainer() {
		try {
			testSU.addContainer("Group1");
		} catch (IllegalAddException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		}
		try {
			testSU.addContainer("Group2");
		} catch (IllegalAddException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		}
		Container g1 = testSU.lookupContainer("Group1");
		Container g2 = testSU.lookupContainer("Group2");
		assertTrue(g1 != null);
		assertTrue(g2 != null);
		
		assertTrue(facade.canRenameContainer(g1, "Group3"));
		assertTrue(facade.canRenameContainer(g2, "Group4"));
		assertFalse(facade.canRenameContainer(g1, "Group2"));
		assertFalse(facade.canRenameContainer(g2, "Group1"));
		
	}

	@Test
	public void testRenameContainer() {
		try {
			testSU.addContainer("Group1");
		} catch (IllegalAddException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		}
		Container g1 = testSU.lookupContainer("Group1");
		assertTrue(testSU.lookupContainer("Group1") != null);
		assertTrue(testSU.lookupContainer("New") == null);
		
		try {
			facade.renameContainer(g1, "New");
		} catch (IllegalEditException e) {
			e.printStackTrace();
			fail("IllegalEditException");
		} catch (NotFoundException e) {
			e.printStackTrace();
			fail("NotFoundException");
		} catch (SaveException e) {
			e.printStackTrace();
			fail("SaveException");
		}
		
		assertTrue(testSU.lookupContainer("Group1") == null);
		assertTrue(testSU.lookupContainer("New") != null);
	}

	@Test
	public void testCanAddStorageUnit() {
		assertTrue(facade.canAddStorageUnit("Test"));
		assertFalse(facade.canAddStorageUnit("RootStorageUnit"));
		try {
			facade.addStorageUnit("Test");
		} catch (IllegalAddException | SaveException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		}
		assertFalse(facade.canAddStorageUnit("Test"));
	}

	@Test
	public void testAddStorageUnit() {
		try {
			facade.addStorageUnit("ThisIsATest");
		} catch (IllegalAddException | SaveException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		}
		
		assertTrue(facade.get_rootUnit().containsContainer("ThisIsATest"));
		assertTrue(facade.get_rootUnit().lookupContainer("ThisIsATest") != null);
		assertFalse(facade.get_rootUnit().containsContainer("NotHere"));
		assertTrue(facade.get_rootUnit().lookupContainer("NotHere") == null);	
	}

	@Test
	public void testCanAddProductGroup() {
		assertTrue(facade.canAddProductGroup(testSU, "Group1"));
		assertTrue(facade.canAddProductGroup(testSU, "Group2"));
		
		try {
			facade.addProductGroup(testSU, "Group1","3","count");
		} catch (IllegalAddException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		} catch (NotFoundException e) {
			e.printStackTrace();
			fail("NotFoundException");
		} catch (SaveException e) {
			e.printStackTrace();
			fail("SaveException");
		}
		
		assertTrue(facade.canAddProductGroup(testSU, "Group2"));
		assertFalse(facade.canAddProductGroup(testSU, "Group1"));		
	}

	@Test
	public void testAddProductGroup() {
		Container pg = null;
		try {
			pg = facade.addProductGroup(testSU, "Group1","3","count");
		} catch (IllegalAddException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		} catch (NotFoundException e) {
			e.printStackTrace();
			fail("NotFoundException");
		} catch (SaveException e) {
			e.printStackTrace();
			fail("SaveException");
		}
		
		assertTrue(testSU.lookupContainer("Group1") != null);
		assertTrue(testSU.lookupContainer("Group2") == null);
		assertEquals(new Amount("3","count"),((ProductGroup)pg).getThreeMonthSupply());
		
		try {
			facade.addProductGroup(testSU, "Group2", "3","count");
		} catch (IllegalAddException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		} catch (NotFoundException e) {
			e.printStackTrace();
			fail("NotFoundException");
		} catch (SaveException e) {
			e.printStackTrace();
			fail("SaveException");
		}
		
		assertTrue(testSU.lookupContainer("Group2") != null);
		
		try{
			facade.addProductGroup(testSU, "Group3", "-1","liters");
			fail("Did not throw exception");
		}
		catch (IllegalAddException e) {}
		catch (NotFoundException e) {
			fail("Wasn't expecting this exception");
		} catch (SaveException e) {
			fail("Wasn't expecting this exception");
		}
		
		try{
			facade.addProductGroup(testSU, "Group3", null, "kilograms");
			fail("Did not throw exception");
		}
		catch (IllegalAddException e) {}
		catch (NotFoundException e) {
			fail("Wasn't expecting this exception");
		} catch (SaveException e) {
			fail("Wasn't expecting this exception");
		}		
	}

	@Test
	public void testCanRemoveContainer() {
		Container testPG = null;
		
		assertFalse(testSU.canRemoveContainer("Group1"));
		assertFalse(testSU.canRemoveContainer("Group2"));
		
		try {
			testPG = facade.addProductGroup(testSU, "ATest","3","count");
			testSU.addContainer("Group2");
		} catch (IllegalAddException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		} catch (NotFoundException e) {
			e.printStackTrace();
			fail("NotFoundException");
		} catch (SaveException e) {
			e.printStackTrace();
			fail("SaveException");
		}
		assert(testPG != null);
		
		assertTrue(facade.canRemoveContainer(testPG));
		
	}

	@Test
	public void testRemoveContainer() {
		assertFalse(testSU.containsContainer("AGroup"));
		try {
			facade.addProductGroup(testSU, "AGroup", "3","count");
		} catch (IllegalAddException e) {
			e.printStackTrace();
			fail("IllegalAddException");
		} catch (NotFoundException e) {
			e.printStackTrace();
			fail("NotFoundException");
		} catch (SaveException e) {
			e.printStackTrace();
			fail("SaveException");
		}
		
		assertTrue(testSU.containsContainer("AGroup"));
		Container group1 = testSU.lookupContainer("AGroup");
		assertTrue(group1 != null);
		
		try {
			facade.removeContainer(group1);
		} catch (IllegalRemoveException e) {
			e.printStackTrace();
			fail("IllegalRemoveException");
		} catch (NotFoundException e) {
			e.printStackTrace();
			fail("NotFoundException");
		} catch (SaveException e) {
			e.printStackTrace();
			fail("SaveException");
		}
		
		assertFalse(testSU.containsContainer("AGroup"));
	}
	
	@Test
	public void testValidThreeMonthSupply(){
		assertTrue(facade.validThreeMonthSupply("30","kilograms"));
		assertTrue(facade.validThreeMonthSupply("0.655","gallons"));
		assertTrue(facade.validThreeMonthSupply("0","fluid ounces"));
		assertFalse("No negatives",facade.validThreeMonthSupply("-1","liters"));
		assertFalse("Count must be integer",facade.validThreeMonthSupply("0.5","count"));
		assertFalse("Invalid quantity",facade.validThreeMonthSupply("abc", "kilograms"));
		assertFalse("Invalid unit",facade.validThreeMonthSupply("3.0", "www"));
		assertFalse("null quantity",facade.validThreeMonthSupply(null, "kilogram"));
		assertFalse("null unit",facade.validThreeMonthSupply("3.0", null));
	}
	
	@Test
	public void testCanSetThreeMonthSupply() throws IllegalAddException{
		Container pg = testSU.addContainer("my prod group");
		
		assertTrue(facade.canSetThreeMonthSupply(pg, "30","kilograms"));
		assertFalse(facade.canSetThreeMonthSupply(pg, "-1","liters"));
		assertFalse(facade.canSetThreeMonthSupply(pg, "0.5","count"));
		assertFalse(facade.canSetThreeMonthSupply(testSU, "30","kilograms"));
		assertFalse(facade.canSetThreeMonthSupply(facade.get_rootUnit(), "30","kilograms"));
		assertFalse(facade.canSetThreeMonthSupply(pg, "abc", "kilograms"));
		assertFalse(facade.canSetThreeMonthSupply(pg, "30", "www"));
	}
	
	@Test
	public void testSetThreeMonthSupply() throws IllegalAddException{
		Container pg = testSU.addContainer("my prod group");
		Amount validA = new Amount(30, UnitOfMeasurement.kilograms);
		
		try {
			facade.setThreeMonthSupply(pg, "30","kilograms");
		} catch (IllegalArgumentException | SaveException e1) {
			fail("Exception");
		}
		
		assertEquals(validA,((ProductGroup)pg).getThreeMonthSupply());
		
		try{
			facade.setThreeMonthSupply(pg, "-1","liters");
			fail("Didn't throw expected exception");
		}
		catch (IllegalArgumentException e) {} catch (SaveException e) {
			fail("Wrong exception");
		}
		
		try{
			facade.setThreeMonthSupply(pg, "0.5","count");
			fail("Didn't throw expected exception");
		}
		catch (IllegalArgumentException e) {} catch (SaveException e) {
			fail("Wrong Exception");
		}	
		
		try{
			facade.setThreeMonthSupply(testSU, "30","kilograms");
			fail("Didn't throw expected exception");
		}
		catch (IllegalArgumentException e) {} catch (SaveException e) {
			fail("Wrong Exception");
		}		
	}
	
	@Test
	public void testGet3MonthStuff() throws IllegalAddException, NotFoundException, SaveException{
		Container pg = 
				facade.addProductGroup(testSU, "PG", "3", "fluid ounces");
		
		assertEquals("3.0",facade.get3MonthSupplyQuantity(pg));
		assertEquals("fluid ounces",facade.get3MonthSupplyUnit(pg));
	
	}
	
}
